--文件分离处理
--由于luajit和lua5.1.4在读取大数据文件时，会出现constant table overflow错误(luajit下报错为：function has more than 65536 constants)
--所以用该模块将过大的配置表文件进行分离成多个文件,
--在用户程序读取配置文件的时候再进行重组
---------------------------------(注)----------------------------------------------
--luajit 和 lua5.1.4 的constant 越界表现并不一致,前者为function中，constant上限
--后者为该层表中constant上限。而且constant定义也完全不一致，
--前者:有非表元素的表,function,指向constant的string型key... 
--后者:每个key,string,bool,而number，table，function不算constant，多个string，bool算一个...
--总而言之,并不能很精确算出constant的数量，只是大概计算表的规模，然后进行拆分
----------------------------------------------------------------------------------——


local MAX_TABLE_REFERENCE = 65535 --luajit的最大表引用数:2^16; lua5.1.4:2^18

--------------------------------------------------------------------------------
--直接分离
--------------------------------------------------------------------------------
--将配置文件分离成多个文件
--SplitLimit:引用上限数,填0将默认为65535
function SplitCfgFile( FinalTable, OutputName, TableSerializeFunc, SplitLimit )
	assert(type(FinalTable)=="table")
	assert(type(OutputName)=="string")
	assert(type(TableSerializeFunc)=="function")
	assert(type(SplitLimit)=="number")

	local ReferenceNum = 1 --引用统计(传进来的表占一个)
	local TbOutput = {}
	local TbSplitPoint = {}
	local TbRoute = {}
	local OutTable = {}
	local ReferenceLimit = ((SplitLimit > 2) and (SplitLimit < MAX_TABLE_REFERENCE)) and SplitLimit or MAX_TABLE_REFERENCE --引用上限，到达上限就拆分表

	--持久化文件
	OutputName = string.gsub( OutputName, ".lua", "" )
	function Serialize2File()
		for Index, TbFile in ipairs(OutTable) do
			if Index == 1 then --第一个文件作特殊处理
				TbFile["_SPLIT_NUM_"] = #OutTable --记录切分数量
				print( "开始导出文件", OutputName..".lua" )
				TableSerializeFunc(TbFile, OutputName..".lua")
			else
				TbFile["__SPLIT_ROUNT__"] = TbSplitPoint[Index-1] 	 --记录切分位置
				print( "开始导出文件", OutputName.."_split_"..(Index-1)..".lua" )
				TableSerializeFunc(TbFile, OutputName.."_split_"..(Index-1)..".lua")
			end
		end
		print( "导出结束！" )
	end

	--将当前的内容保存的导出到临时表
	function AddToOutTable()
		print( "加入导出表,引用数：", ReferenceNum )
		table.insert(TbSplitPoint, table.deep_clone(TbRoute)) --记录切分位置
		table.insert( OutTable, TbOutput )
		TbOutput = {}
	end

	--保存到TbOutput
	function set2TbOutput( Key, value )
		local tb = TbOutput
		for _, K in ipairs(TbRoute) do
			tb[K] = tb[K] or {}
			tb = tb[K]
		end
		tb[Key] = value
	end

	--分割
	function Split2File( Table )
		local Flag = false
		for Key, Info in pairs(Table) do
			if type(Info) == "table" then
				if not Flag then
					ReferenceNum = ReferenceNum+1 --表中有表计数+1
					Flag = true
				end
				ReferenceNum = ReferenceNum+1
				if ReferenceNum >= ReferenceLimit-1 then
					AddToOutTable()
					ReferenceNum = 1
				end
				set2TbOutput( Key, {} )
				table.insert( TbRoute, Key )
				Split2File(Info)
			else
				set2TbOutput( Key, Info )
			end
		end
		table.remove( TbRoute, #TbRoute )
	end

	Split2File( FinalTable )
	AddToOutTable()
	Serialize2File()
end

--计算引用总数
local SumReference = 0 --总引用数
local function CountReference( Table )
	SumReference = SumReference + 1
	local flag = true
	for _, v in pairs(Table) do
		if type(v) == "table" then
			CountReference( v )
			if flag then -- 表中有表计数+1
				SumReference = SumReference +1
				flag = false
			end
		end
	end
end

--------------------------------------------------------------------------------
--重组加载文件
--------------------------------------------------------------------------------
function LoadCfgFile( CfgPath )
	print( "CfgPath:", CfgPath )
	local MainCfg = safe_dofile(CfgPath)
	assert( MainCfg, "加载失败！" )
	PathNoSuffix = string.gsub( CfgPath, ".lua", "" )

	--先将被拆分的表合回去
	function MergeSplitTable( SplitCfg, Split_Rount )
		if #Split_Rount < 1 then return end
		local Tb = SplitCfg
		local TbTarget = MainCfg
		for Index, key in ipairs(Split_Rount) do
			Tb = Tb[key]
			TbTarget = TbTarget[key]

			if Tb then
				if TbTarget then
					for k,v in pairs(Tb) do
						if (not Split_Rount[Index+1]) or k ~= Split_Rount[Index+1] then --被切分的位置要逐步合过去,否则会被覆盖
							TbTarget[k] = v
						end
					end
				end
			end
		end

		SplitCfg[Split_Rount[1]] = nil --合完清掉该字段
	end

	function LoadSplitFile( SplitCfgPath )
		local SplitCfg = safe_dofile(SplitCfgPath)
		local Split_Rount = SplitCfg["__SPLIT_ROUNT__"]
		if Split_Rount then
			SplitCfg["__SPLIT_ROUNT__"] = nil --清空额外字段
			MergeSplitTable( SplitCfg, Split_Rount ) --先将被拆分的表合回去
			for k,v in pairs(SplitCfg) do --将剩余的全部合过去
				MainCfg[k] = v
			end
		end
	end

	local SplitNum = MainCfg["_SPLIT_NUM_"]

	if SplitNum and SplitNum > 1 then --如果表被拆分了的话
		for i=1, SplitNum-1 do
			LoadSplitFile( PathNoSuffix.."_split_"..i..".lua" )
		end
	end
	MainCfg["_SPLIT_NUM_"] = nil --清空额外字段
	return MainCfg
end

--两个表进行对比
local count = 0
function compare2Table( TableA, TableB )
	count = count +1
	for k,v in pairs(TableA) do
		if TableB[k] then
			if type(TableA[k]) == "table" and type(TableB[k]) == "table" then
				compare2Table( TableA[k], TableB[k] )
			else
				if type(TableA[k]) ~= "function" and type(TableB[k]) ~= "function" then
					assert( TableA[k] == TableB[k], "###:"..k )
				end
			end
		else
			print( "####errrrrrr!:", k )
			return
		end
	end
	return count
end







